package com.dxg.msns.gateway.filter;

import com.dxg.msns.auth.utils.JwtUtils;
import com.dxg.msns.gateway.config.FilterProperties;
import com.dxg.msns.gateway.config.JwtProperties;
import com.dxg.msns.gateway.utils.TokenUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
@EnableConfigurationProperties({ JwtProperties.class, FilterProperties.class })
public class LoginFilter implements GlobalFilter, Ordered {
    private static final Logger logger = LoggerFactory.getLogger(LoginFilter.class);

    @Autowired
    private JwtProperties jwtProperties;

    @Autowired
    private FilterProperties filterProperties;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 获取请求路径
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getURI().getPath();
        System.out.println("Processing request for path: {}"+ path);

        // 检查是否为内部服务调用
        if (isInternalServiceCall(request)) {
            System.out.println("Internal service call detected for path: {}, allowing access"+ path);
            return chain.filter(exchange);
        }

        // 检查是否为登录相关接口（绝对白名单）
        if (isAuthenticationEndpoint(path)) {
            System.out.println("Authentication endpoint {}, allowing access without token"+ path);
            return chain.filter(exchange);
        }

        // 判断配置的白名单
        for (String allowPath : this.filterProperties.getAllowPaths()) {
            if (path.startsWith(allowPath)) {
                System.out.println("Path {} is in whitelist, allowing access"+path);
                return chain.filter(exchange);
            }
        }

        // 获取token - 优先从Authorization请求头获取，Cookie作为备用
        String token = TokenUtils.getTokenFromRequest(request, jwtProperties.getCookieName());
        if (token == null) {
            System.out.println("No token found in Authorization header or Cookie for path: {}"+ path);
            return createUnauthorizedResponse(exchange);
        }

        // 验证Token格式
        if (!TokenUtils.isValidTokenFormat(token)) {
            System.out.println("Invalid token format for path: {}"+path);
            return createUnauthorizedResponse(exchange);
        }

        try {
            // 校验token
            if (jwtProperties.getPublicKey() == null) {
                logger.error("Public key is not initialized");
                return createServerErrorResponse(exchange);
            }

            // 验证Token并获取用户信息
            JwtUtils.getInfoFromToken(token, jwtProperties.getPublicKey());
            System.out.println("Token validation successful for path: {}"+path);
            return chain.filter(exchange);

        } catch (Exception e) {
            System.out.println("Token validation failed for path: {},"+ path+" error: {}"+ e.getMessage());
            return createForbiddenResponse(exchange);
        }
    }

    /**
     * 检查是否为内部服务调用
     * 可以通过User-Agent、来源IP或特殊Header来识别
     * 
     * @param request HTTP请求
     * @return 是否为内部调用
     */
    private boolean isInternalServiceCall(ServerHttpRequest request) {
        // 方法1：检查User-Agent是否为Feign
        String userAgent = request.getHeaders().getFirst("User-Agent");
        if (userAgent != null && userAgent.contains("Java")) {
            System.out.println("Detected Java client, likely internal service call");
            return true;
        }

        // 方法2：检查是否有内部服务标识头
        String internalHeader = request.getHeaders().getFirst("X-Internal-Request");
        if ("true".equals(internalHeader)) {
            System.out.println("Internal request header detected");
            return true;
        }

        // 方法3：检查请求来源是否为内部网络（简单示例）
        String remoteAddr = request.getRemoteAddress() != null
                ? request.getRemoteAddress().getAddress().getHostAddress()
                : "";
        if (remoteAddr.startsWith("127.0.0.1") || remoteAddr.startsWith("localhost")) {
            System.out.println("Request from localhost, likely internal call");
            return true;
        }

        return false;
    }

    /**
     * 检查是否为认证相关接口（不需要Token验证）
     * 
     * @param path 请求路径（已经过StripPrefix处理）
     * @return 是否为认证接口
     */
    private boolean isAuthenticationEndpoint(String path) {
        // 登录相关接口列表（经过StripPrefix处理后的路径）
        String[] authEndpoints = {
                "/login", // 对应原始路径 /api/auth/login
                "/accredit", // 对应原始路径 /api/auth/accredit
                "/register", // 对应原始路径 /api/auth/register（如果有）
                "/deleteCookie" // 对应原始路径 /api/auth/deleteCookie
        };

        for (String endpoint : authEndpoints) {
            if (path.equals(endpoint)) {
                return true;
            }
        }

        return false;
    }

    /**
     * 创建401未授权响应
     */
    private Mono<Void> createUnauthorizedResponse(ServerWebExchange exchange) {
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        return response.setComplete();
    }

    /**
     * 创建403禁止访问响应
     */
    private Mono<Void> createForbiddenResponse(ServerWebExchange exchange) {
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(HttpStatus.FORBIDDEN);
        return response.setComplete();
    }

    /**
     * 创建500服务器错误响应
     */
    private Mono<Void> createServerErrorResponse(ServerWebExchange exchange) {
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
        return response.setComplete();
    }

    @Override
    public int getOrder() {
        return 5;
    }
}
